/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.jeff.springWebTestNG.concurrent;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class is to emulate Semaphore behavior to interpret its feature ('permit' based synchronization)
 * 
 * @author zhou.zhefu
 */
public class SemaphoreBoundedSetEmulator {
    private static final int N_CAP = 3;
    private static final int R_INT = 10;
    private volatile boolean stop = false;
    SemaphoreBoundedSet<Integer> set = new SemaphoreBoundedSet<Integer>(N_CAP);
    Random r = new Random();
    
    
    public void start() {
        try {
            Thread t11 = new Adder();
            Thread t12 = new Adder();
            Thread t13 = new Adder();
            Thread t21 = new Remover();
            Thread t22 = new Remover();
            
            t11.start();
            t12.start();
            t13.start();
            t21.start();
            t22.start();
            
            Thread.sleep(50);
            stop = true;
            
            t11.join();
            t12.join();
            t13.join();
            t21.join();
            t22.join();
            
            System.out.println("Store current size=" + set.currentSize());
        } catch (InterruptedException ex) {
            Logger.getLogger(SemaphoreBoundedSetEmulator.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    private class Remover extends Thread {

        @Override
        public void run() {
            while(!stop) {
                int nextInt = r.nextInt(R_INT);
                boolean removed = set.remove(nextInt);
                if (removed) 
                    System.out.println(nextInt + " was removed. permits=" + set.sema.availablePermits());
            }
        }
        
    }
    
    private class Adder extends Thread {

        @Override
        public void run() {
            while(!stop) {
                int nextInt = r.nextInt(R_INT);
                boolean added = set.add(nextInt);
                if (added) 
                    System.out.println(nextInt + " was added. permits=" + set.sema.availablePermits());
            }
        }
        
    }
    
    private class SemaphoreBoundedSet<T> {
        private Set<T> store = new HashSet<T>();
        Semaphore sema;

        public SemaphoreBoundedSet(int capacity) {
            sema = new Semaphore(capacity); //no FIFO guarantee with this constructor
        }

        public boolean add(T o) {
            boolean wasAdded = false;
            try {
                sema.acquire(); //block until permits are available

                wasAdded = store.add(o);
            } catch (InterruptedException ex) {
                wasAdded = false;
                sema.release();
                Logger.getLogger(SemaphoreBoundedSet.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                return wasAdded;
            }
        }

        public boolean remove(T o) {
            boolean wasRemoved = false;
            try {
                wasRemoved = store.remove(o);
                if (wasRemoved) 
                    sema.release();
            } finally {
                return wasRemoved;
            }
        }
        
        public int currentSize() {
            return store.size();
        }
    }
}
